#include <linux/module.h>
#include <linux/kernel.h>

#include <crypto/akcipher.h>
#include <crypto/hash.h>
#include <crypto/public_key.h>
#include <crypto/sig.h>
#include <linux/kernel_read_file.h>
#define CLIENT_CERT_PATH "../tls_handshake_ko/cert/ecdsa_server.crt"

int sha256(char *plaintext, int psize, u8 *output)
{
	struct crypto_shash *tfm;
	struct shash_desc *shash;
	int ret;

	tfm = crypto_alloc_shash("sha256", 0, 0);
	if (IS_ERR(tfm)) {
		printk("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm));
		return PTR_ERR(tfm);
	}

	shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
			GFP_KERNEL);
	if (!shash) {
		ret = -ENOMEM;
		goto failed;
	}

	shash->tfm = tfm;

	ret = crypto_shash_digest(shash, plaintext, psize, output);

	kfree(shash);

failed:
	crypto_free_shash(tfm);
	return ret;
}
static int sign_init(void)
{
	struct crypto_sig *sig;
	void *cert;
	char hash[32];
	int ret;
    int keylen = kernel_read_file_from_path(CLIENT_CERT_PATH, 0, &cert, INT_MAX, NULL,
                                                 READING_X509_CERTIFICATE);
    printk("private_key length: %d\n", keylen);
    if (keylen < 0) return -1;

	sig = crypto_alloc_sig("ecdsa-nist-p256", 0, 0);

    if (IS_ERR(sig)) {
        ret = PTR_ERR(sig);
        return -1;
    }
    
    print_hex_dump(KERN_CONT, "pubkey:", DUMP_PREFIX_OFFSET, 16, 1, (char *)cert + 143, 65, false);
    ret = crypto_sig_set_pubkey(sig, (char *)cert + 143, 65);
	printk("crypto_sig_set_pubkey ret:%d\n", ret);
	if (ret < 0)
	    return -1;

    print_hex_dump(KERN_CONT, "sig:", DUMP_PREFIX_OFFSET, 16, 1, (char *)cert + 308, 71, false);
    sha256((char *)cert + 4, 289, hash);
    print_hex_dump(KERN_CONT, "tbs:", DUMP_PREFIX_OFFSET, 16, 1, (char *)cert + 4, 289, false);
    print_hex_dump(KERN_CONT, "hash:", DUMP_PREFIX_OFFSET, 16, 1,  hash, 32, false);
	ret = crypto_sig_verify(sig, (char *)cert + 308, 71, hash, 32);
	printk("crypto_sig_verify ret:%d\n", ret);
	return 0;
}

static void sign_exit(void) {
	pr_info("Goodbye cruel world...\n");
}

module_init(sign_init);
module_exit(sign_exit);

MODULE_LICENSE("GPL");
